import fs from 'fs';
import yaml from 'yaml';

interface IMetaFile {
    fileFormatVersion: number;
    guid: string;
    timeCreated: number;
    licenseType: 'Pro';
    MonoImporter: {
        serializedVersion: number;
        defaultReferences: []
        executionOrder: number;
        icon: { instanceID: number };
        userData: string;
        assetBundleName: string;
        assetBundleVariant: string;
    }
}

interface IPrefabElement {
    yamlID: number;
    fileID: string;
    range: {
        start: number;
        end: number;
    }
    rawContents: string;
    obj?: object;
}

interface IMonoBehaviour {
    MonoBehaviour: object;
}

interface ISelectable extends IMonoBehaviour {
    MonoBehaviour: {
        m_GameObject: {
            fileID: string;
        }
    }
}

export class PrefabEditor {
    private activeToggleGUID!: string;

    private elements: IPrefabElement[] = [];
    private elemMap: { [fileID: string]: IPrefabElement} = {};

    constructor() {
        yaml.scalarOptions.int.asBigInt = true;
    }

    public readGUID(filePath: string) {
        let fileContent = fs.readFileSync(filePath, 'utf-8');
        let meta = yaml.parse(fileContent) as IMetaFile;
        this.activeToggleGUID = meta.guid;
    }

    public edit(filePath: string) {
        let fileContent = fs.readFileSync(filePath, 'utf-8');
        
        this.elements.length = 0;
        this.elemMap = {};

        let fileLines = fileContent.split(/[\r\n]+/);
        let elem: IPrefabElement;
        // 前两行为tag跳过
        for(let i = 2, len = fileLines.length; i < len; i++) {
            let line = fileLines[i];
            let docMatchRst = line.match(/--- !u!(\d+) &(\d+)/);
            if(docMatchRst) {
                // 匹配到新元素
                if(elem!) {
                    elem.range.end = i;
                }
                elem = this.newElement(Number(docMatchRst[1]), docMatchRst[2], i + 1);
                this.elemMap[elem.fileID] = elem;
                this.elements.push(elem);
            } else {
                if(elem!) {
                    elem.rawContents += line + '\n';
                }
            }
        }

        // 检查seletable
        fileContent = this.removeSeletable(fileContent);

        fs.writeFileSync(filePath, fileContent, 'utf-8');
    }

    private newElement(yamlID: number, fileID: string, start: number): IPrefabElement {
        return {
            yamlID,  fileID, 
            range: {
                start, 
                end: start
            }, 
            rawContents: ''
        };
    }

    private removeSeletable(fileContent: string): string {
        for(let i = 0, len = this.elements.length; i < len; i++) {
            let elem = this.elements[i];
            if(elem.rawContents.includes('m_Script: {fileID: 11500000, guid: ' + this.activeToggleGUID + ', type: 3}')) {
                // 这是ActiveToggle
                let normalRst = elem.rawContents.match(/normalState: \{fileID: (\d+)\}/)!;
                let normalFileID = normalRst[1];
                if(normalFileID != '0') {
                    fileContent = this.removeOneSelectable(normalFileID, fileContent);
                }

                let selectedRst = elem.rawContents.match(/selectedState: \{fileID: (\d+)\}/)!;
                let selectedFileID = selectedRst[1];
                if(selectedFileID != '0') {
                    fileContent = this.removeOneSelectable(selectedFileID, fileContent);
                }
            }
        }
        return fileContent;
    }

    private removeOneSelectable(seletableID: string, fileContent: string) {
        let elem = this.elemMap[seletableID];
        console.assert(elem != null, `seletable not exists: ${seletableID}`);
        if(!elem.obj) {
            elem.obj = yaml.parse(elem.rawContents);
        }
        // 删除Selectable组件
        fileContent = fileContent.replace('--- !u!' + elem.yamlID + ' &' + elem.fileID + '\n' + elem.rawContents, '');
        // 删除组件引用
        let reg = new RegExp('  - component: \\{fileID: ' + elem.fileID + '\\}\\r?\n', 'g');
        fileContent = fileContent.replace(reg, '');
        // 替换组件引用
        fileContent = fileContent.replace(seletableID, (elem.obj as ISelectable).MonoBehaviour.m_GameObject.fileID);
        return fileContent;
    }
}